package q2d.client.controllers;

import java.net.InetAddress;
import java.util.ArrayList;

import q2d.client.renderers.GFactory;
import q2d.client.worlds.GameWorld;
import q2d.common.CollisionDetector;
import q2d.common.models.GameContainer;
import q2d.common.models.HitsContainer;
import q2d.common.models.units.Bullet;
import q2d.common.models.units.Hit;
import q2d.common.models.units.Player;
import q2d.common.models.units.PlayerHit;
import q2d.common.models.units.WallHit;
import q2d.netWrap.Ncontroller;
import q2d.netWrap.Npacket;
import q2d.netWrap.Nwrapper;

import com.brackeen.javagamebook.input.InputManager;

/** NetworkController:
 * Odpowiada za odbieranie pakietow sieciowych od serwera i odpowiednia ich
 * interpretacje. Uaktualnia listy jednostek w GameWorld'zie.
 * 
 */
public class NetworkController extends Controller {

	private final int DISCONNECT_TIMEOUT = 500000;

	// kontroler komunikacji sieciowej (odpowiada za wysylanie i odbieranie pakietow)
	private Ncontroller ncontroller;

	// referencja swiat a gry:
	private GameWorld gameWorld;

	// referencje obslugiwanych kontrolerow
	private FXController fxController;

	// IP serwera
	private InetAddress serverIP; 
	private int serverPort;
	
	private int lastPacketTime = DISCONNECT_TIMEOUT;// czas od otrzymania ostatniego pakietu
	public boolean isServerConnection = false;

	/**
	 * Konstruktor.
	 * @param game_world
	 * @param game_width
	 * @param game_height
	 */
	public NetworkController(int game_width, int game_height, GameWorld game_world, FXController fx_controller, int listening_port, int dispatching_port, InetAddress server_ip){
		super(game_width, game_height);
		gameWorld = game_world;
		fxController = fx_controller;

		serverIP = server_ip;
		serverPort = dispatching_port;
		//try {
			//serverIP = InetAddress.getByAddress(new byte[]{(byte)192,(byte)168,(byte)2,(byte)142});
			//serverIP = InetAddress.getByAddress(new byte[]{(byte)192,(byte)168,(byte)2,(byte)142});
		//} catch (Exception exception){}

		ncontroller = new Ncontroller(listening_port, 10);

		// testowy komentarz
	}



	@Override
	public void controll(long elapsed_time) {
		// symulacja
		Npacket packet;
		Nwrapper wrapper;

		// --------------------------------------
		// 			obsluga informacji od serwera
		// --------------------------------------
		while((packet = ncontroller.pop())!=null){
			wrapper = (Nwrapper)ncontroller.retrieveObject(packet);

			if (wrapper == null){
				//System.out.print(123);
			}

			switch (wrapper.getType()){
				case 1:
					// kontener graczy i pociskow
					handleGameContainer((GameContainer)wrapper.getObject());
					break;
				case 2:
					// kontener trafien
					handleHitsContainer((HitsContainer)wrapper.getObject());
					break;
			}

			// otrzymano pakiet (jest polaczenie z serwerem)
			lastPacketTime = DISCONNECT_TIMEOUT;
		}

		// testowanie polaczenia z serwerem
		lastPacketTime -= elapsed_time;
		if (lastPacketTime < 0){
			isServerConnection = false;
		} else {
			isServerConnection = true;
		}

		// ----------------------------------------
		// 			wysylanie informacji do serwera
		// ----------------------------------------
		GameContainer sendContainer = new GameContainer(gameWorld.thisBulletsList.size(), 1);
		//Container sendContainer = new Container();

		sendContainer.addPlayer(gameWorld.thisPlayer);

		for (int i=0; i<gameWorld.thisBulletsList.size(); i++){
			sendContainer.addBullet(gameWorld.thisBulletsList.get(i));
		}
		// wyczyszczenie listy pociskow tego gracza
		gameWorld.thisBulletsList.clear();

		//gameWorld.debugString = String.valueOf(gameWorld.thisBulletsList.size()+" / odebrane: "+gameWorld.playersList.size());//+String.valueOf(gameWorld.othersBulletsList.size())+;
		ncontroller.sendObject(sendContainer, 1, serverIP, serverPort);
	}



	@Override
	public void disableControll() {
		ncontroller.close();
	}



	@Override
	public void enableControll() {
		ncontroller.listen();
	}



	/**
	 * Interpretuje informacje o pociskach i graczach z kontenera.
	 * @param gameContainer
	 */
	private void handleGameContainer(GameContainer gameContainer){
		gameWorld.playersList.clear();
		gameWorld.othersBulletsList.clear();

		// informacje o graczach
		for (int i=0; i<gameContainer.getPlayersCount(); i++){
			Player p = gameContainer.getPlayer(i);
			gameWorld.playersList.add(p);
		}

		// informacje o pociskach w grze
		for (int i=0; i<gameContainer.getBulletsCount(); i++){
			Bullet b = gameContainer.getBullet(i);
			gameWorld.othersBulletsList.add(b);
		}

		// informacje o flagach
		gameWorld.blueFlag.x = gameContainer.blueFlagX;
		gameWorld.blueFlag.y = gameContainer.blueFlagY;
		gameWorld.redFlag.x = gameContainer.redFlagX;
		gameWorld.redFlag.y = gameContainer.redFlagY;

		// informacje o punktacji
		if (gameWorld.thisPlayer.team == Player.BLUE_TEAM){
			// gracz jest NIEBIESKI
			if (gameWorld.blueTeamScore < gameContainer.blueTeamScore){
				// NIEBIESCY punktuja
				fxController.addPlayerMessage(gameWorld.thisPlayer.x, gameWorld.thisPlayer.y, GFactory.IMG_MESSAGE_YEAH);
			}
			if (gameWorld.redTeamScore < gameContainer.redTeamScore){
				// CZERWONI punktuja
				fxController.addPlayerMessage(gameWorld.thisPlayer.x, gameWorld.thisPlayer.y, GFactory.IMG_MESSAGE_ARGH);
			}
		} else {
			// gracz jest CZERWONY
			if (gameWorld.redTeamScore < gameContainer.redTeamScore){
				// CZERWONI punktuja
				fxController.addPlayerMessage(gameWorld.thisPlayer.x, gameWorld.thisPlayer.y, GFactory.IMG_MESSAGE_YEAH);
			}
			if (gameWorld.blueTeamScore < gameContainer.blueTeamScore){
				// NIEBIESCY punktuja
				fxController.addPlayerMessage(gameWorld.thisPlayer.x, gameWorld.thisPlayer.y, GFactory.IMG_MESSAGE_ARGH);
			}
		}
		gameWorld.blueTeamScore = gameContainer.blueTeamScore;
		gameWorld.redTeamScore = gameContainer.redTeamScore;

		// informacje o HP
		gameWorld.thisPlayer.health = gameContainer.playerHP;

		//gameWorld.debugString = String.valueOf("ZGONS: "+gameContainer.playerDeathCounter+" HP: "+gameContainer.playerHP);
		gameWorld.thisPlayer.deathsCounter = gameContainer.playerDeathCounter;
	}



	/**
	 * Interpretuje informacje o trafieniach z kontenera.
	 * @param gameContainer
	 */
	private void handleHitsContainer(HitsContainer hitsContainer){
		int i;
		PlayerHit ph;
		WallHit wh;

		// obsluga trafien w graczy
		for (i=0; i<hitsContainer.getPlayerHitsCount(); i++){
			ph = hitsContainer.getPlayerHit(i);
			//fxController.addPlayerHit(ph.x, ph.y);
			fxController.addPlayerMessage(ph.x, ph.y, GFactory.IMG_MESSAGE_FUCK);
		}

		// obsluga trafien w sciany
		for (i=0; i<hitsContainer.getWallHitsCount(); i++){
			wh = hitsContainer.getWallHit(i);
			fxController.addWallHitExplosion(wh.x, wh.y, wh.vx, wh.vy);
		}
	}
}